#ifndef SDLPL_SIMPLE_NETH
#define SDLPL_SIMPLE_NETH

#include "../core/InterData.h"
#include "../core/AbstractObject.h"
#include "../core/ObjectFactory.h"
#include <string>
#include <queue>
#include "SDL.h"
#include "SDL_net.h"

#define MAX_SIMPLE_TCP_SLOTS 100
#define SIMPLE_TCP_SLOT_BUFFER_SIZE 100000
#define PLSDLNET_MAX_PACKET_SIZE 64000

class SimpleTCPConnection;

class MessageContainer
{
public:
	IData data;
	std::string name;
};

class PLSDLNetUtils
{
public:
	static int canParse(unsigned char* buffer, int datalength, int& errornum);
	static IData parseAndConsume(unsigned char* buffer, int buffersize, int& bytesconsumed, std::string& msgtypename);
	static int idataToBytes(unsigned char* buffer, int buffersize, IData& val);
	static IData idataFromBytes(unsigned char* buffer, int datasize, int& bytesconsumed);
	static int serializeMessage(unsigned char* buffer, int buffersize, IData& val, std::string& msgTypeName);
	static int netWriteByte(unsigned char* dest, unsigned char val, int dpos, int buffersize);
	static int netWriteDouble(unsigned char* dest, double val, int dpos, int buffersize);
	static int netWriteInt(unsigned char* dest, int val, int dpos, int buffersize);
	static int netWriteString(unsigned char* dest, std::string& val, int dpos, int buffersize);
	static int netWriteShortString(unsigned char* dest, std::string& val, int dpos, int buffersize);
	static int netReadByte(unsigned char* src, unsigned char& val, int spos, int buffersize);
	static int netReadDouble(unsigned char* src, double& val, int spos, int buffersize);
	static int netReadInt(unsigned char* src, int& val, int spos, int buffersize);
	static int netReadString(unsigned char* src, std::string& val, int spos, int buffersize);
	static int netReadShortString(unsigned char* src, std::string& val, int spos, int buffersize);
};

class SimpleTCPSlot
{
private:	
	SimpleTCPConnection* _parent;
	std::string _name;
	int _slotId;
	bool _isRunning;
	
	SDL_mutex* _sendLock;
	SDL_mutex* _recvLock;
	
	unsigned char* _sendBuffer;
	unsigned char* _recBuffer;
	
	TCPsocket _sock;
	SDL_Thread* _clientThread;
	std::vector<MessageContainer> _outMessages;
	
	static int clientMain(void* owner);
public:
	SimpleTCPSlot(SimpleTCPConnection *parent, TCPsocket sock, std::string name, int slotId);
	~SimpleTCPSlot();

	void close();
	void sendMessage(std::string& msgTypeName, IData& msg);
	std::vector<MessageContainer>* getMessages();
	const std::string& getName();
	void setName(const std::string& name);
};

class STCMessageHandler
{
public:
	AnyAOPtr target;
	std::string targetFunc;
};

// these tell liautogen.py how to deal with IData types
//PUSH_FUNC IData
//pushIData(L, ret);

//PUSH_FUNC IData&
//pushIData(L, ret);

//POP_FUNC IData&
//IData a%(argPos)d = popIData(L, %(luaPos)d);

//POP_FUNC IData
//IData a%(argPos)d = popIData(L, %(luaPos)d);

class SimpleTCPConnection : public AbstractObject
{
public:
	SimpleTCPConnection();

	// SimpleTCPConnection specific stuff
	//--OBJTYPE SimpleTCPConnection
	//--LIAG_THESE
	void listenForConnections(int port);
	void connect(std::string& hostname, int port);
	void disconnect(int slot);
	void sendMessage(int targetSlot, std::string& msgTypeName, IData& msg);
	void addMessageHandler(std::string& recName, AnyAOPtr obj);
	void removeMessageHandler(AnyAOPtr obj);
	void addConnectionHandler(std::string& recName, AnyAOPtr obj);
	void removeConnectionHandler(AnyAOPtr obj);
	void renameConnection(int slot, std::string& newname);
	std::string getConnectionName(int slot);
	//--STOP_LIAG
	void killSlot(SimpleTCPSlot* slobj, int id);
	static int listenMain(void* self);
	void addClientSlot(TCPsocket clientSock, std::string clientName);

	// General abstract object stuff goes below

	// Return the c++ type id
	virtual int getCppTypeId();
	// Returns whether this can be cast to a given type
	virtual bool isType(int id);

	// Return the c++ type as a string 
	virtual std::string getCppTypeName();

	// returns a description of this thing
	virtual std::string getDescription();

	// create another one of these
	virtual AbstractObject* newInstance();

	// init & update
	virtual void init();
	virtual void update(double dt);

	// push its static functions into the given lua table
	virtual void pushStaticFuncs(lua_State* L);

	virtual ~SimpleTCPConnection();
private:
	void sendMessagesToHandlers(std::vector<MessageContainer>* msgs, int srcSlot);
	std::vector<STCMessageHandler> _msgHandlers;
	std::vector<STCMessageHandler> _connHandlers;
	bool _isServer;
	SimpleTCPSlot* _slots[MAX_SIMPLE_TCP_SLOTS];
	//TCPsocket _supersock;
	SDL_Thread* _listenThread;
	int _listenport;
};

typedef struct {
	UDPsocket sock;
	IPaddress addr;	
} UDPConnection;

typedef struct {
	IPaddress addr;
	int srcID;
	std::string connName;
} UDPPeer;

class SimpleUDPConnection : public AbstractObject
{
public:
	SimpleUDPConnection();
	~SimpleUDPConnection();

	//OBJTYPE SimpleUDPConnection
	//LIAG_THESE
	int openConnection(std::string& connStr, int port, int srcID);
	bool allowIncomingConnections(int port);
	bool forbidIncomingConnections(int port);
	void closeConnection(int connID);
	void sendMessage(int connDest, std::string& msgTypeName, IData& msg, bool reliable);
	void addMessageHandler(std::string& recName, AnyAOPtr obj);
	void removeMessageHandler(AnyAOPtr obj);
	std::string getConnName(int peerid);
	void setConnName(int peerid, std::string newname);
	void initNetworking();
	//STOP_LIAG
	int openConnection(IPaddress& ip, int srcID);

	// General abstract object stuff goes below

	// Return the c++ type id
	virtual int getCppTypeId();
	// Returns whether this can be cast to a given type
	virtual bool isType(int id);

	// Return the c++ type as a string 
	virtual std::string getCppTypeName();

	// returns a description of this thing
	virtual std::string getDescription();

	// create another one of these
	virtual AbstractObject* newInstance();

	// init & update
	virtual void init();
	virtual void update(double dt);

	// push its static functions into the given lua table
	virtual void pushStaticFuncs(lua_State* L);
private:
	void sendMessageToHandlers(MessageContainer& msg, std::string& srcName, int srcSlot);
	bool openSocket(UDPConnection &c, int port);
	void closeSocket(UDPConnection &c);

	std::vector<STCMessageHandler> _msgHandlers;

	void serializePacket(MessageContainer& msg, UDPpacket *p);
	bool deserializePacket(UDPpacket *p, MessageContainer& msg);

	//std::queue<MessageContainer> _messages;

	UDPPeer _peerTable[MAX_SIMPLE_TCP_SLOTS];
	UDPConnection _connTable[MAX_SIMPLE_TCP_SLOTS];
	UDPpacket* _packet;
};

extern RegisterCppObj<SimpleUDPConnection> g_register_simpleudpconnection;
extern RegisterCppObj<SimpleTCPConnection> g_register_simpletcpconnection;

#endif